/**
 * Anderground project - Subway maps processor application.
 * Copyright (C) 2008-2009 Eldar Abusalimov, Ilya Shurigyn
 * 
 * This file is part of Anderground.
 * 
 * Anderground is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * Anderground is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Anderground.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.anderground.modules.drawer.dynamic;

import java.util.ArrayList;
import java.util.HashMap;

import org.anderground.core.drawer.Calculator;
import org.anderground.core.drawer.Calculator.Mode;
import org.anderground.core.graphics.Canvas;
import org.anderground.core.graphics.PointF;
import org.anderground.core.model.Hub;
import org.anderground.core.model.Station;

/**
 * This class represents such entities as Station or Hub
 * 
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
class Crossing {

	/** the Amoeba to call the draw method */
	private Amoeba rootAmoeba;

	/** array of primitive Amoebas that make up this Amoeba */
	private HashMap<Station, SimpleAmoeba> simpleAmoebasMap;

	/** neighbor-to-link mapping */
	private HashMap<Crossing, Road> bacillusCrossingMap = new HashMap<Crossing, Road>();

	private Structure structure;

	/**
	 * Complex Amoeba constructor
	 * 
	 * @param hub
	 * @param mode
	 * @param structure
	 */
	public Crossing(Hub hub, Mode mode, Structure structure) {
		this.structure = structure;
		structure.addCrossing(this);
		simpleAmoebasMap = new HashMap<Station, SimpleAmoeba>(hub
				.getStations().size(), 1);
		Amoeba[] amoebas = new Amoeba[hub.getStations().size()];
		int i = 0;
		for (Station station : hub.getStations()) {
			int color;
			if (station.getLines().length == 1) {
				color = station.getLines()[0].getColor();
			} else {
				color = Config.DEFAULT_COLOR;
			}
			SimpleAmoeba simpleAmoeba = new SimpleAmoeba(this,
					station, color, new PointF(station, mode));
			simpleAmoebasMap.put(station, simpleAmoeba);
			amoebas[i] = simpleAmoeba;
			i++;
		}

		int level = amoebas.length;
		float minDistance;
		float distance;
		int nearestJ = 0, nearestK = 1;
		while (level > 1) {
			minDistance = Float.POSITIVE_INFINITY;
			for (int j = 0; j < level; j++) {
				Amoeba evenAmoeba = amoebas[j];
				for (int k = j + 1; k < level; k++) {
					Amoeba oddAmoeba = amoebas[k];
					distance = computeDistance(evenAmoeba, oddAmoeba);
					if (distance < minDistance) {
						minDistance = distance;
						nearestJ = j;
						nearestK = k;
					}

				}
			}
			CompoundAmoeba compoundAmoeba = new CompoundAmoeba(
					amoebas[nearestJ], amoebas[nearestK]);
			if (nearestJ != level - 1) {
				amoebas[nearestJ] = compoundAmoeba;
				amoebas[nearestK] = amoebas[level - 1];
			} else {
				amoebas[nearestK] = compoundAmoeba;
			}

			level--;
			amoebas[level] = null;
		}

		rootAmoeba = amoebas[0];

	}

	/**
	 * Simple Amoeba constructor
	 * 
	 * @param station
	 * @param mode
	 * @param structure
	 */
	public Crossing(Station station, Mode mode, Structure structure) {
		this.structure = structure;
		structure.addCrossing(this);
		simpleAmoebasMap = new HashMap<Station, SimpleAmoeba>(1, 1);
		int color;
		if (station.getLines().length == 1) {
			color = station.getLines()[0].getColor();
		} else {
			color = Config.DEFAULT_COLOR;
		}
		SimpleAmoeba simpleAmoeba = new SimpleAmoeba(this, station,
				color, new PointF(station, mode));
		simpleAmoebasMap.put(station, simpleAmoeba);
		rootAmoeba = simpleAmoeba;
	}

	private static final PointF tempCoordF = new PointF();

	private static float computeDistance(Amoeba evenAmoeba, Amoeba oddAmoeba) {
		PointF.averaged(evenAmoeba.coordF, oddAmoeba.coordF, evenAmoeba.size,
				oddAmoeba.size, tempCoordF);

		float maxDistance = Float.NEGATIVE_INFINITY;
		float distance;
		for (PointF eachCoordF : evenAmoeba.coords) {
			distance = tempCoordF.distanceTo(eachCoordF);
			if (distance > maxDistance) {
				maxDistance = distance;
			}
		}
		for (PointF eachCoordF : oddAmoeba.coords) {
			distance = tempCoordF.distanceTo(eachCoordF);
			if (distance > maxDistance) {
				maxDistance = distance;
			}
		}

		return maxDistance;
	}

	public Road searchBacillusCrossing(Crossing neighborCrossing) {
		Road road = bacillusCrossingMap.get(neighborCrossing);
		if (road == null) {
			road = new Road(this, neighborCrossing, structure);
		}
		return road;
	}

	void addNeighbor(Crossing neighborCrossing, Road road) {
		this.bacillusCrossingMap.put(neighborCrossing, road);
	}

	public void applyCalculator(Calculator calculator) {
		rootAmoeba.applyCalculator(calculator);
	}

	public void addDrawnAmoebas(ArrayList<Amoeba> drawnAmoebas) {
		rootAmoeba.addDrawnAmoebas(drawnAmoebas);
	}

	public void draw(Canvas canvas) {
		rootAmoeba.draw(canvas);
	}

	public HashMap<Station, SimpleAmoeba> getSimpleAmoebasMap() {
		return simpleAmoebasMap;
	}

}
